package com.wei.czz.config;

import com.wei.czz.common.property.ThreadProperty;
import lombok.Getter;
import lombok.Setter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author wei
 * date: 2021-09-14 14:42:00
 * className: ThreadConfig 线程池配置类
 * version: 1.0
 * description:
 */
@Getter
@Setter
@Configuration
@EnableAsync    // 开启线程池异步执行
public class ThreadConfig {

    /**
     * 将自定义ThreadPoolTaskExecutor线程池添加到Spring容器中
     */
    @Bean("threadExecutor")
    public ThreadPoolTaskExecutor threadExecutor(ThreadProperty threadProperty) {

        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        /*
            1、线程池非核心线程存活时间采用默认的60秒
            2、核心线程跑满后，新任务加入队列，且队列达到容量阈值，才会创建非核心线程执行新任务
            3、非核心线程被创建后，空闲时间达到默认值后，将被系统回收
            4、如果任务队列容量大于0，底层使用LinkedBlockingQueue作为任务队列，否则使用SynchronousQueue作为任务队列
            5、ThreadPoolTaskExecutor线程池采用‘装饰’设计模式，对JDK自带的ThreadPoolExecutor线程池进行二次封装，线程池执行逻辑仍然采用ThreadPoolExecutor的代码
         */
        // 设置线程池核心线程数
        executor.setCorePoolSize(threadProperty.getCoreSize());
        // 设置线程池持有最大线程数
        executor.setMaxPoolSize(threadProperty.getMaxSize());
        // 设置任务队列大小
        executor.setQueueCapacity(threadProperty.getQueueCapacity());
        // 设置线程名称前缀
        executor.setThreadNamePrefix(threadProperty.getThreadNamePrefix());
        // 设置线程池任务拒绝策略
        executor.setRejectedExecutionHandler(this.getRejectedExecutionHandler(threadProperty));
        // 初始化线程池
        executor.initialize();

        return executor;
    }

    /**
     * 将自定义ThreadPoolTaskExecutor线程池添加到Spring容器中
     */
    @Bean("delayExecutor")
    public ScheduledThreadPoolExecutor delayExecutor(ThreadProperty threadProperty) {

        ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(threadProperty.getCoreSize());
        // 设置线程生产工厂对象
        executor.setThreadFactory(new CzzThreadFactory(threadProperty.getDelayThreadNamePrefix()));

        return executor;
    }

    /**
     * 根据配置文件配置的任务拒绝策略数值，创建对应的任务拒绝策略对象
     */
    private RejectedExecutionHandler getRejectedExecutionHandler(ThreadProperty threadProperty) {
        RejectedExecutionHandler policy;
        switch (threadProperty.getRejectType()) {
            case 1:
                policy = new ThreadPoolExecutor.AbortPolicy();
                break;
            case 2:
                policy = new ThreadPoolExecutor.DiscardPolicy();
                break;
            case 3:
                policy = new ThreadPoolExecutor.DiscardOldestPolicy();
                break;
            case 0:
            default:
                policy = new ThreadPoolExecutor.CallerRunsPolicy();
                break;
        }
        return policy;
    }

    /**
     * 自定义线程工厂类
     */
    private static class CzzThreadFactory implements ThreadFactory {

        private final ThreadGroup group;

        private final AtomicInteger threadNumber = new AtomicInteger(1);

        private final String threadNamePrefix;

        CzzThreadFactory(String threadNamePrefix) {
            SecurityManager s = System.getSecurityManager();
            group = (s != null) ? s.getThreadGroup() :
                    Thread.currentThread().getThreadGroup();
            this.threadNamePrefix = threadNamePrefix;
        }

        public Thread newThread(Runnable r) {
            Thread t = new Thread(group, r,
                    threadNamePrefix + threadNumber.getAndIncrement(),
                    0);
            if (t.isDaemon())
                t.setDaemon(false);
            if (t.getPriority() != Thread.NORM_PRIORITY)
                t.setPriority(Thread.NORM_PRIORITY);
            return t;
        }
    }

}
